﻿#include "elk_download_manager.h"

ElkDownloadTask::ElkDownloadTask(QObject *parent) :
    QObject(parent)
{

}

ElkDownloadTask::~ElkDownloadTask()
{

}

void ElkDownloadTask::start(const QString &url,
                            const QString &fileName,
                            QNetworkAccessManager *network)
{
    if (network_) {
        return;
    }

    url_ = url;
    fileName_ = fileName;
    network_ = network;
    connect(network_, SIGNAL(destroyed(QObject *)),
            this, SLOT(networkDestroyed(QObject *)));

    resetRequest();
}

void ElkDownloadTask::abort()
{
    if (reply_) {
        abortReply(); //触发replyFinished
    }
}

void ElkDownloadTask::retry()
{
    if (!reply_) {
        resetRequest();
    }
}

void ElkDownloadTask::clear()
{
    clearFile(); //删除临时文件
    abortReply(); //触发replyFinished
}

bool ElkDownloadTask::fileOpen()
{
    if (!file_.isOpen()) {
        file_.setFileName(fileName_ + ".tmp");
        file_.open(QFile::WriteOnly | QFile::Truncate);
    }
    return file_.error() == QFile::NoError;
}

bool ElkDownloadTask::fileWrite()
{
    if (file_.error() == QFile::NoError) {
        file_.write(reply_->readAll());
    }
    return file_.error() == QFile::NoError;
}

bool ElkDownloadTask::fileRename()
{
    if (file_.error() == QFile::NoError) {
        file_.rename(fileName_);
    }
    return file_.error() == QFile::NoError;
}

void ElkDownloadTask::clearFile()
{
    if (file_.exists()) {
        file_.remove();
    }
}

void ElkDownloadTask::abortReply()
{
    if (reply_ && reply_->isRunning()) {
        reply_->abort();
    }
}

bool ElkDownloadTask::complete()
{
    if (bytesReceived_ == bytesTotal_ &&
            ((bytesFileSize_ > 0 && bytesFileSize_ == file_.size()) ||
                (bytesTotal_ == file_.size()))) {
        return true;
    }
    return false;
}

void ElkDownloadTask::resetRequest()
{
    QNetworkRequest request(url_);
    request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, QVariant("true")); //允许重定向
    if (file_.isOpen()) {
        QString range = QString("bytes=%1-").arg(file_.size());
        request.setRawHeader(QByteArray("Range"), range.toLatin1());
    }

    reply_ = network_->get(request); //reply_ 由 network 管理
    reply_->ignoreSslErrors();

    connect(reply_, SIGNAL(metaDataChanged()),
            this, SLOT(metaDataChanged()));
    connect(reply_, SIGNAL(finished()),
            this, SLOT(replyFinished()));
    connect(reply_, SIGNAL(readyRead()),
            this, SLOT(replyReadyRead()));
    connect(reply_, SIGNAL(redirected(const QUrl &)),
            this, SLOT(replyRedirected(const QUrl &)));
    connect(reply_, SIGNAL(downloadProgress(qint64, qint64)),
            this, SLOT(replyDownloadProgress(qint64, qint64)));
    connect(reply_, SIGNAL(destroyed(QObject *)),
            this, SLOT(replyDestroyed(QObject *)));
}

void ElkDownloadTask::metaDataChanged()
{
    qDebug() << "========================================";

    auto url = reply_->url().toString();
    qDebug() << "Request" << url;

    auto request = reply_->request();
    auto headerList1 = request.rawHeaderList();
    for (auto header : headerList1) {
        qDebug() << header << " : " << request.rawHeader(header);
    }

    auto status = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toString();
    auto reason = reply_->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
    qDebug() << "Response" << status << reason;

    auto headerList2 = reply_->rawHeaderList();
    for (auto header : headerList2) {
        qDebug() << header << " : " << reply_->rawHeader(header);
    }

    qDebug() << "========================================";

    auto rangeHeader = QByteArray("Content-Range");
    if (reply_->hasRawHeader(rangeHeader)) {
        auto rangeValue = reply_->rawHeader(rangeHeader);
        auto pos = rangeValue.lastIndexOf('/');
        auto fileSize = rangeValue.mid(pos + 1);
        bytesFileSize_ = fileSize.toLongLong();
    } else {
        bytesFileSize_ = 0;
        file_.close(); //确保触发 fileOpen() 让文件内容为空
    }
}

void ElkDownloadTask::replyFinished()
{
    if (!file_.error() && !reply_->error()) {
        if (fileOpen() && fileWrite()) { //防止特殊情况（如文件大小为 0 时确保能生成一个空文件）
            if (complete() && fileRename()) {
                emit this->finished(); //确保文件下载完成的情况下下才发送 finished 信号
                return;
            }
        }
    }

    if (file_.error()) {
        emit this->fileError(file_.error(), file_.errorString());
        return;
    }
    if (reply_->error()) {
        emit this->replyError(reply_->error(), reply_->errorString());
        return;
    }
    emit this->interrupted(); //其它未知情形
}

void ElkDownloadTask::replyReadyRead()
{
    if (!fileOpen()) {
        return abortReply(); //触发replyFinished
    }
    if (!fileWrite()) {
        return abortReply(); //触发replyFinished
    }
}

void ElkDownloadTask::replyRedirected(const QUrl &url)
{
    emit this->urlChanged(url.toString());
}

void ElkDownloadTask::replyDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
    bytesTotal_ = bytesTotal;
    bytesReceived_ = bytesReceived;
    emit this->downloadProgress(bytesReceived, bytesTotal, bytesFileSize_);
}

void ElkDownloadTask::replyDestroyed(QObject *obj)
{
    if (obj == reply_) {
        reply_ = nullptr;
    }
}

void ElkDownloadTask::networkDestroyed(QObject *obj)
{
    if (obj == network_) {
        network_ = nullptr;
    }
}

ElkDownloadManager::ElkDownloadManager(QObject *parent) :
    QObject(parent),
    network_(new QNetworkAccessManager(this))
{
    connect(network_, SIGNAL(finished(QNetworkReply *)),
            this, SLOT(replyFinished(QNetworkReply *)));
}

ElkDownloadManager::~ElkDownloadManager()
{

}

ElkDownloadTask *ElkDownloadManager::createDownloadTask(const QString &url,
                                                        const QString &fileName)
{
    ElkDownloadTask *task = new ElkDownloadTask(this);
    task->start(url, fileName, network_);
    tasks_.append(task);
    connect(task, SIGNAL(destroyed(QObject *)),
            this, SLOT(taskDestroyed(QObject *)));
    return task;
}

void ElkDownloadManager::deleteDownloadTask(ElkDownloadTask *task)
{
    tasks_.removeOne(task);
    task->deleteLater();
}

void ElkDownloadManager::taskDestroyed(QObject *obj)
{
    tasks_.removeOne((ElkDownloadTask*)obj);
}

void ElkDownloadManager::replyFinished(QNetworkReply *reply)
{
    reply->deleteLater();
}
